#include "Window/PiccoloGLFWWindow.h"

#include "PiccoloLog.h"
#include "Event/PEventDispatcher.h"

#include "GLFW/glfw3native.h"

namespace Piccolo
{
    PiccoloGLFWWindow::PiccoloGLFWWindow(uint32_t width, uint32_t height, const char *title)
    {
        if (!glfwInit())
        {
            LOG_E("Failed to init glfw");
            return;
        }
        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
        glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
        mGLFWWindow = glfwCreateWindow(width, height, title, nullptr, nullptr);
        if (!mGLFWWindow)
        {
            LOG_E("Failed to create glfw window");
            return;
        }

        // 处理下窗口出现的位置
        GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
        if (primaryMonitor)
        {
            int xPos, yPos, workWidth, workHeight;
            glfwGetMonitorWorkarea(primaryMonitor, &xPos, &yPos, &workWidth, &workHeight);
            glfwSetWindowPos(mGLFWWindow, workWidth / 2 - width / 2, workHeight / 2 - height / 2);
        }

        glfwMakeContextCurrent(mGLFWWindow);
        SetupWindowCallbacks();

        glfwShowWindow(mGLFWWindow);
    }

    PiccoloGLFWWindow::~PiccoloGLFWWindow()
    {
        glfwDestroyWindow(mGLFWWindow);
        glfwTerminate();
        LOG_I("The application running end");
    }

    bool PiccoloGLFWWindow::ShouldClose()
    {
        return glfwWindowShouldClose(mGLFWWindow);
    }

    void PiccoloGLFWWindow::PollEvents()
    {
        glfwPollEvents();
    }

    void PiccoloGLFWWindow::SwapBuffer()
    {
        glfwSwapBuffers(mGLFWWindow);
    }

    void PiccoloGLFWWindow::GetMousePos(glm::vec2 &mousePos) const
    {
        double x, y;
        glfwGetCursorPos(mGLFWWindow, &x, &y);
        mousePos = { x, y };
    }

    bool PiccoloGLFWWindow::IsMouseDown(EMouseButton mouseButton) const
    {
        return glfwGetMouseButton(mGLFWWindow, mouseButton) == GLFW_PRESS;
    }

    bool PiccoloGLFWWindow::IsMouseUp(EMouseButton mouseButton) const
    {
        return glfwGetMouseButton(mGLFWWindow, mouseButton) == GLFW_RELEASE;
    }

    bool PiccoloGLFWWindow::IsKeyDown(EKey key) const
    {
        return glfwGetKey(mGLFWWindow, key) == GLFW_PRESS;
    }

    bool PiccoloGLFWWindow::IsKeyUp(EKey key) const
    {
        return glfwGetKey(mGLFWWindow, key) == GLFW_RELEASE;
    }

    void PiccoloGLFWWindow::SetupWindowCallbacks()
    {
        glfwSetWindowUserPointer(mGLFWWindow, this);
        glfwSetFramebufferSizeCallback(mGLFWWindow, [](GLFWwindow* window, int width, int height)
        {
            auto* glfwWindow = static_cast<PiccoloGLFWWindow*>(glfwGetWindowUserPointer(window));
            if (glfwWindow)
            {
                PFrameBufferResizeEvent frameBufferResizeEvent{ static_cast<uint32_t>(width), static_cast<uint32_t>(height) };
                PEventDispatcher::GetInstance()->Dispatch(frameBufferResizeEvent);
            }
        });

        // 窗口大小
        glfwSetWindowSizeCallback(mGLFWWindow, [](GLFWwindow* window, int width, int height)
        {
            auto* glfwWindow = static_cast<PiccoloGLFWWindow*>(glfwGetWindowUserPointer(window));
            if (glfwWindow)
            {
                PWindowResizeEvent windowResizeEvent{ static_cast<uint32_t>(width), static_cast<uint32_t>(height) };
                PEventDispatcher::GetInstance()->Dispatch(windowResizeEvent);
            }
        });

        // 获取/失去焦点
        glfwSetWindowFocusCallback(mGLFWWindow, [](GLFWwindow* window, int focused)
        {
            auto* glfwWindow = static_cast<PiccoloGLFWWindow*>(glfwGetWindowUserPointer(window));
            if (glfwWindow)
            {
                if (focused)
                {
                    PWindowFocusEvent windowFocusEvent{};
                    PEventDispatcher::GetInstance()->Dispatch(windowFocusEvent);
                }
                else
                {
                    PWindowLostFocusEvent windowLostFocusEvent{};
                    PEventDispatcher::GetInstance()->Dispatch(windowLostFocusEvent);
                }
            }
        });

        // 窗口位置变更
        glfwSetWindowPosCallback(mGLFWWindow, [](GLFWwindow* window, int xPos, int yPos)
        {
            auto* glfwWindow = static_cast<PiccoloGLFWWindow*>(glfwGetWindowUserPointer(window));
            if (glfwWindow)
            {
                PWindowMoveEvent windowMoveEvent{ static_cast<uint32_t>(xPos), static_cast<uint32_t>(yPos) };
                PEventDispatcher::GetInstance()->Dispatch(windowMoveEvent);
            }
        });

        // 窗口关闭
        glfwSetWindowCloseCallback(mGLFWWindow, [](GLFWwindow* window)
        {
            auto* glfwWindow = static_cast<PiccoloGLFWWindow*>(glfwGetWindowUserPointer(window));
            if (glfwWindow)
            {
                PWindowCloseEvent windowCloseEvent{};
                PEventDispatcher::GetInstance()->Dispatch(windowCloseEvent);
            }
        });

        // 键盘
        glfwSetKeyCallback(mGLFWWindow, [](GLFWwindow* window, int key, int scancode, int action, int mods)
        {
            auto* glfwWindow = static_cast<PiccoloGLFWWindow*>(glfwGetWindowUserPointer(window));
            if (glfwWindow)
            {
                if (action == GLFW_RELEASE)
                {
                    PKeyInputReleaseEvent keyInputReleaseEvent{ static_cast<EKey>(key) };
                    PEventDispatcher::GetInstance()->Dispatch(keyInputReleaseEvent);
                }
                else
                {
                    PKeyInputPressEvent keyInputPressEvent{ static_cast<EKey>(key), static_cast<EKeyMod>(mods), action == GLFW_REPEAT };
                    PEventDispatcher::GetInstance()->Dispatch(keyInputPressEvent);
                }
            }
        });

        // 鼠标按键
        glfwSetMouseButtonCallback(mGLFWWindow, [](GLFWwindow* window, int button, int action, int mods)
        {
            auto* glfwWindow = static_cast<PiccoloGLFWWindow*>(glfwGetWindowUserPointer(window));
            if (glfwWindow)
            {
                if (action == GLFW_PRESS)
                {
                    PMouseButtonPressEvent mouseButtonPressEvent{ static_cast<EMouseButton>(button), static_cast<EKeyMod>(mods), false };
                    PEventDispatcher::GetInstance()->Dispatch(mouseButtonPressEvent);
                }
                if (action == GLFW_RELEASE)
                {
                    PMouseButtonReleaseEvent mouseButtonReleaseEvent{ static_cast<EMouseButton>(button) };
                    PEventDispatcher::GetInstance()->Dispatch(mouseButtonReleaseEvent);
                }
            }
        });

        // 鼠标位置
        glfwSetCursorPosCallback(mGLFWWindow, [](GLFWwindow* window, double xPos, double yPos)
        {
            auto* glfwWindow = static_cast<PiccoloGLFWWindow*>(glfwGetWindowUserPointer(window));
            if (glfwWindow)
            {
                PMouseMoveEvent mouseMoveEvent{ static_cast<float>(xPos), static_cast<float>(yPos) };
                PEventDispatcher::GetInstance()->Dispatch(mouseMoveEvent);
            }
        });

        // 滚轮
        glfwSetScrollCallback(mGLFWWindow, [](GLFWwindow* window, double xOffset, double yOffset)
        {
            auto* glfwWindow = static_cast<PiccoloGLFWWindow*>(glfwGetWindowUserPointer(window));
            if (glfwWindow)
            {
                PMouseScrollEvent mouseScrollEvent{ static_cast<float>(xOffset), static_cast<float>(yOffset) };
                PEventDispatcher::GetInstance()->Dispatch(mouseScrollEvent);
            }
        });
    }
}
